/*
 * $Id: Identity.java 25 2014-12-29 11:44:57Z ratking $
 *
 * Copyleft (C) 2014 RatKing. All wrongs reserved.
 */
package cn.ratking.demo.jaws7.security.view;

import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
import org.omnifaces.util.Faces;
import org.omnifaces.util.Messages;
import org.slf4j.Logger;

import cn.ratking.demo.jaws7.model.LogEvent;
import cn.ratking.demo.jaws7.security.exception.IncorrectCaptchaException;
import cn.ratking.demo.jaws7.security.vo.Credentials;

/**
 * 登录控制
 */
@Named
@RequestScoped
public class Identity implements Serializable {

    //public static final String HOME_URL = "app/index.xhtml";
    @Inject
    private transient Logger log;
    /** 事件源 */
    @Inject
    private Event<LogEvent> logEventSrc;
    @Inject
    private Credentials credentials;

    /**
     * 用户登录系统
     *
     * @throws IOException
     */
    public void login() throws IOException {
        //log.debug("Debug: Identity.login(), credentials=" + credentials);

        // 用户名
        String username = credentials.getUsername();
        // 口令
        String password = credentials.getPassword();
        // 验证码
        String captcha = credentials.getCaptcha();
        // 记住我
        boolean rememberMe = credentials.isRememberMe();
        try {
            doCaptchaValidate(captcha);

            SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password, rememberMe));
            log.info("用户{}登录成功", username);
            logEventSrc.fire(new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_INFO, "用户" + username + "登录成功"));
            Messages.addGlobalInfo("用户" + username + "登录成功");
            SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(Faces.getRequest());
            Faces.redirect(savedRequest != null ? savedRequest.getRequestUrl() : Faces.getRequest().getContextPath() + "/index.xhtml");
        } catch (IncorrectCaptchaException ice) {
            String errorMsg = "验证码错误，请重试";
            log.error(errorMsg);
            logEventSrc.fire(new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_ERROR, "用户" + username + "登录失败，" + errorMsg));
            Messages.addGlobalError(errorMsg);
        } catch (UnknownAccountException uae) {
            String errorMsg = "未知的帐户，请重试";
            log.error(errorMsg);
            logEventSrc.fire(new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_ERROR, "用户" + username + "登录失败，" + errorMsg));
            Messages.addGlobalError(errorMsg);
        } catch (IncorrectCredentialsException ice) {
            String errorMsg = "错误的口令，请重试";
            log.error(errorMsg);
            logEventSrc.fire(new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_ERROR, "用户" + username + "登录失败，" + errorMsg));
            Messages.addGlobalError(errorMsg);
        } catch (LockedAccountException lae) {
            String errorMsg = "锁定的帐户，请重试";
            log.error(errorMsg);
            logEventSrc.fire(new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_ERROR, "用户" + username + "登录失败，" + errorMsg));
            Messages.addGlobalError(errorMsg);
        } catch (ExcessiveAttemptsException eae) {
            String errorMsg = "过多的尝试，请重试";
            log.error(errorMsg);
            logEventSrc.fire(new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_ERROR, "用户" + username + "登录失败，" + errorMsg));
            Messages.addGlobalError(errorMsg);
        } catch (AuthenticationException ae) {
            String errorMsg = "验证异常，请重试";
            log.error(errorMsg);
            logEventSrc.fire(new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_ERROR, "用户" + username + "登录失败，" + errorMsg));
            Messages.addGlobalError(errorMsg);
        }
    }

    /**
     * 校验验证码
     *
     * @see https://github.com/axet/kaptcha
     *
     * @param captcha 用户输入的验证码
     * @throws IncorrectCaptchaException 验证码校验不通过异常
     */
    private void doCaptchaValidate(String captcha) throws IncorrectCaptchaException {
        HttpSession session = Faces.getSession();
        Enumeration<String> enumeration = session.getAttributeNames();
        while (enumeration.hasMoreElements()) {
            String attributeName = enumeration.nextElement();
            log.debug("Debug: session(" + attributeName + ")=>" + session.getAttribute(attributeName));
        }

        String generatedKey =(String) session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
        log.debug("Debug: doCaptchaValidate(), generatedKey=" + generatedKey);

        String generatedKey2 =(String) session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_CONFIG_KEY);
        log.debug("Debug: doCaptchaValidate(), generatedKey2=" + generatedKey2);

        //String code = (String) session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
        // 系统生成的kaptcha验证码
        String code = (String) session.getAttribute("code");
        log.debug("Debug: doCaptchaValidate(), code=" + code + ", captcha=" + captcha);
        if (!captcha.equalsIgnoreCase(code)) {
            throw new IncorrectCaptchaException("验证码错误！");
        }
    }

    /**
     * 用户退出系统
     *
     * @throws IOException
     */
    public void logout() throws IOException {
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        log.debug("Debug: Identity.logout(), username=" + username);
        if (SecurityUtils.getSubject().getPrincipal() == null) {
            String warnMsg = "尚未登录，无需退出";
            log.warn(warnMsg);
            logEventSrc.fire(new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_WARN, "用户" + username + "，" + warnMsg));
            Messages.addGlobalWarn(warnMsg);
            return;
        }
        // removes all identifying information and invalidates their session too.
        SecurityUtils.getSubject().logout();
        Faces.invalidateSession();
        log.info("用户{}退出成功", username);
        LogEvent logEvent = new LogEvent(LogEvent.LOGTYPE_SECURITY, LogEvent.LOGLEVEL_INFO, "用户" + username + "退出成功");
        logEvent.setUserId(username);
        logEvent.setUserName(username);
        logEventSrc.fire(logEvent);
        Messages.addGlobalInfo("用户{0}退出成功", username);
        Faces.redirect(Faces.getRequest().getContextPath() + "/index.xhtml");
    }
}
